package main

import (
	"flag"
	"fmt"
	"log/slog"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/xuender/kit/pools"
)

type service struct {
	ports   []int
	timeout int64
	pool    *pools.Native[string, bool]
}

func (p *service) pingPort(host string, port, idx int) bool {
	timeout := time.Duration(p.timeout) * time.Millisecond

	conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), timeout)
	if err != nil {
		return false
	}

	defer conn.Close()

	slog.Info("is alive", "index", idx, "host", host, "port", port)

	return true
}

func (p *service) ping(host string, idx int) bool {
	for _, port := range p.ports {
		if p.pingPort(host, port, idx) {
			return true
		}
	}

	return false
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

// nolint: gomnd
func main() {
	svr := service{timeout: 500}
	ports := "22,25,80,135,443,445,631"
	subnet := "192.168.1.0/24"
	parallel := 30

	flag.Usage = usage
	flag.StringVar(&subnet, "subnet", subnet, "subnet to scan")
	flag.Int64Var(&svr.timeout, "timeout", svr.timeout, "timeout in milliseconds")
	flag.StringVar(&ports, "ports", ports, "ports to scan")
	flag.IntVar(&parallel, "parallel", parallel, "parallel num")
	flag.Parse()

	list := strings.Split(ports, ",")
	svr.ports = make([]int, len(list))

	for idx, port := range list {
		svr.ports[idx], _ = strconv.Atoi(port)
	}

	svr.pool = pools.NewNative(uint(parallel), svr.ping)

	host, ipnet, err := net.ParseCIDR(subnet)
	if err != nil {
		slog.Error("Error parsing", "subnet", subnet, "error", err)

		return
	}

	ips := []string{}
	for ip := host.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
		ips = append(ips, ip.String())
	}

	svr.pool.Post(ips...)
}

func usage() {
	fmt.Fprintf(os.Stderr, "pingall\n\n")
	fmt.Fprintf(os.Stderr, "ping subnet all host.\n\n")
	fmt.Fprintf(os.Stderr, "Usage: %s [flags]\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(1)
}
